package net.sf.rose.task;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import net.sf.rose.jdbc.KeyGenerator;
import net.sf.rose.jdbc.dao.BeanDAO;
import net.sf.rose.jdbc.query.BeanSQL;
import net.sf.rose.jdbc.service.Service;
import net.sf.rose.util.DataUtils;
import net.sf.rose.util.DateFormat;

/**
 * 类说明：开机定时执行任务通用类
 * <p>
 * 可以指定任务在启动后立即执行一次
 * <p>
 * 可以指定任务在某个特定的时间执行一次
 * <p>
 * 可以指定任务在个时间后每隔一段时间(秒、分钟、小时、天、周、月)后重复执行
 * 
 * @author fengjian
 * @since 2019/06/20
 */
public class RoseTaskThread implements Runnable {

	private static Logger LOG = Logger.getLogger(RoseTaskThread.class);

	/** 任务定义类 */
	private RoseTaskBean bean = null;

	/** 任务执行类 */
	private RoseTask task = null;

	/** 任务继续有效 */
	private boolean continueDo = true;

	/** 在运行中 */
	private boolean isRunning = false;

	/** 线程下次要执行的时间 */
	private Date excuteDateTime = null;

	/**
	 * 获取任务定义
	 * 
	 * @return 任务定义
	 */
	public RoseTaskBean getRoseTaskBean() {
		return bean;
	}

	/**
	 * 设置任务是否继续执行下去
	 * 
	 * @param continueDo 继续执行
	 */
	public void setContinueDo(boolean continueDo) {
		this.continueDo = continueDo;
	}

	/**
	 * 返回线程是否在执行中
	 * 
	 * @return 线程是否在执行中
	 */
	public boolean isRunning() {
		return isRunning;
	}

	/**
	 * 获取线程下次要执行的时间
	 * 
	 * @return
	 */
	public Date getNextExcuteTime() {
		return this.excuteDateTime;
	}

	/**
	 * 获取当前所有线程
	 * 
	 * @return
	 */
	public static List<RoseTaskThread> getAllThreadList() {
		List<RoseTaskThread> list = new ArrayList<>();
		for (RoseTaskThread task : allThreadMap.values()) {
			list.add(task);
		}
		return list;
	}

	/**
	 * 根据线程编号获取线程
	 * 
	 * @return
	 */
	public static RoseTaskThread getThread(RoseTaskBean bean) {
		return allThreadMap.get(bean.getTaskID());
	}

	/**
	 * 启动执行的方法
	 * 
	 * @param bean 任务定义类
	 * @return 线程是否开启成功
	 */
	public void start(RoseTaskBean bean) {
		this.bean = bean;
		if (bean.getTaskID() == null) {
			bean.setTaskID(KeyGenerator.getSystemUUID());
		}
		String cn = bean.getClassName();
		task = DataUtils.getInstance(this.getClass(), cn);
		if (bean.getDescription() == null) {
			bean.setDescription(task.getClass().getName());
		}
		int cycleType = bean.getCycleType();
		// 如果是定时周期执行
		if (cycleType > RoseTaskBean.CYCLE_All) {
			// 根据设定的执行时间，设置下一次执行的时间
			Calendar setDate = Calendar.getInstance();
			setDate.setTime(bean.getExcuteDateTime());
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			calendar.set(Calendar.MILLISECOND, 0);
			// 如果是每月执行一次，那么设置本月执行的那天时间
			if (cycleType == RoseTaskBean.CYCLE_MONTH) {
				calendar.set(Calendar.DAY_OF_MONTH, setDate.get(Calendar.DAY_OF_MONTH));
				calendar.set(Calendar.HOUR_OF_DAY, setDate.get(Calendar.HOUR_OF_DAY));
				calendar.set(Calendar.MINUTE, setDate.get(Calendar.MINUTE));
				calendar.set(Calendar.SECOND, setDate.get(Calendar.SECOND));
			}
			// 如果是每周几执行一次，那么设置本周执行的那天时间
			else if (cycleType == RoseTaskBean.CYCLE_WEEK) {
				calendar.set(Calendar.DAY_OF_WEEK, bean.getCycleTime());
				calendar.set(Calendar.HOUR_OF_DAY, setDate.get(Calendar.HOUR_OF_DAY));
				calendar.set(Calendar.MINUTE, setDate.get(Calendar.MINUTE));
				calendar.set(Calendar.SECOND, setDate.get(Calendar.SECOND));
			}
			// 每天
			else if (cycleType == RoseTaskBean.CYCLE_DAY) {
				calendar.set(Calendar.HOUR_OF_DAY, setDate.get(Calendar.HOUR_OF_DAY));
				calendar.set(Calendar.MINUTE, setDate.get(Calendar.MINUTE));
				calendar.set(Calendar.SECOND, setDate.get(Calendar.SECOND));
			}
			// 每小时
			else if (cycleType == RoseTaskBean.CYCLE_HOUR) {
				calendar.set(Calendar.MINUTE, setDate.get(Calendar.MINUTE));
				calendar.set(Calendar.SECOND, setDate.get(Calendar.SECOND));
			}
			// 每分
			else if (cycleType == RoseTaskBean.CYCLE_MINITUE) {
				calendar.set(Calendar.SECOND, setDate.get(Calendar.SECOND));
			}
			bean.setExcuteDateTime(calendar.getTime());
		}
		Thread t = new Thread(this);
		t.start();
		LOG.warn("定时任务" + bean.getDescription() + "执行...");
	}

	private static Map<String, RoseTaskThread> allThreadMap = new HashMap<>();

	@Override
	public void run() {
		// 开始运行任务
		allThreadMap.put(bean.getTaskID(), this);
		excuteDateTime = bean.getExcuteDateTime();
		int cycleType = bean.getCycleType();
		String description = bean.getDescription();
		// 如果只执行一次就退出循环
		if (cycleType == RoseTaskBean.CYCLE_ONCE) {
			// 计算本次要休眠到指定时间再执行
			long d1 = (new Date()).getTime();
			long d2 = excuteDateTime == null ? d1 : excuteDateTime.getTime();
			d2 = d2 - d1;
			if (d2 > 0) {
				try {
					int time = (int) (d2 / 1000);
					String date = DateFormat.format(excuteDateTime, DateFormat.DATE_FORMAT_3);
					LOG.warn(description + "将休眠" + time + "秒后" + date + "执行...");
					Thread.sleep(d2);
				} catch (InterruptedException e) {
					LOG.error(e.getMessage(), e);
				}
			}
			if (continueDo) {
				this.excute();
			}
		} else {
			do {
				// 如果有时间执行周期
				if (cycleType > RoseTaskBean.CYCLE_All) {
					// 计算下次开始执行的时间点
					excuteDateTime = getNextExcuteTime(excuteDateTime);
					// 计算本次要休眠到指定时间再执行
					long d1 = (new Date()).getTime();
					long d2 = excuteDateTime.getTime();
					d2 = d2 - d1;
					if (d2 > 0) {
						try {
							int time = (int) (d2 / 1000);
							String date = DateFormat.format(excuteDateTime, DateFormat.DATE_FORMAT_3);
							LOG.warn(description + "将休眠" + time + "秒后" + date + "执行...");
							Thread.sleep(d2);
						} catch (InterruptedException e) {
							LOG.error(e.getMessage(), e);
						}
					}
					if (continueDo) {
						this.excute();
					}
				}
				// 如果没有时间执行周期，简单重复执行
				else {
					if (continueDo) {
						this.excute();
					}
				}
			} while (continueDo);
		}
		// 结束运行任务
		allThreadMap.remove(bean.getTaskID());
	}

	private static Map<String, String> currentRunningThreadMap = new ConcurrentHashMap<>();

	/**
	 * 判断指令执行类名的任务是否正在执行
	 * 如果正在执行，则不能再次开启包含同样的执行类的线程
	 * 
	 * @param className - 执行类名
	 * @return
	 */
	private static synchronized boolean isThreadRunning(String className) {
		String str = currentRunningThreadMap.get(className);
		if (str != null) {
			return true;
		}
		currentRunningThreadMap.put(className, className);
		return false;
	}

	/**
	 * 执行线程业务
	 */
	private void excute() {
		// 如果同类线程正在执行
		String className = this.bean.getClassName();
		if (isThreadRunning(className)) {
			System.out.println("线程" + className + "正在执行，请等待执行完毕后重试...");
			return;
		}
		isRunning = true;
		Service service = new Service();
		BeanDAO dao = new BeanDAO(service);
		BeanSQL query = dao.getQuerySQL();
		query.setEntityClass(RoseTaskLogBean.class);
		RoseTaskLogBean log = new RoseTaskLogBean();
		// 记录开始任务
		try {
			log.setTaskID(bean.getTaskID());
			log.setStartTime(new Date());
			query.createSaveSql(log);
			dao.update();
			service.commit();
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return;
		}
		String result = "成功";
		String message = null;
		// 执行任务
		try {
			message = task.excute(service);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			result = "失败";
			message = e.getMessage();
			service.rollback();
		}
		// 记录结束任务
		try {
			log.setEndTime(new Date());
			log.setResult(result);
			log.setMessage(message);
			query.createUpdateSql(log);
			dao.update();
			service.commit();
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} finally {
			service.close();
		}
		// 标记本线程暂时不在执行中...
		isRunning = false;
		currentRunningThreadMap.remove(className);
	}

	/**
	 * 计算下次开始执行的时间点
	 * 
	 * @param date 这次时间
	 * @return 下次开始执行的时间点
	 */
	private Date getNextExcuteTime(Date date) {
		int cycleType = bean.getCycleType();
		int cycleTime = bean.getCycleTime();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		// calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		// 如果当前时间晚于计划时间，使用下一个计划时间
		if ((new Date()).getTime() > date.getTime()) {
			if (cycleType == RoseTaskBean.CYCLE_SECOND) {
				calendar.add(Calendar.SECOND, cycleTime);
			} else if (cycleType == RoseTaskBean.CYCLE_MINITUE) {
				calendar.add(Calendar.MINUTE, cycleTime);
			} else if (cycleType == RoseTaskBean.CYCLE_HOUR) {
				calendar.add(Calendar.HOUR_OF_DAY, cycleTime);
			} else if (cycleType == RoseTaskBean.CYCLE_DAY) {
				calendar.add(Calendar.DATE, cycleTime);
			} else if (cycleType == RoseTaskBean.CYCLE_WEEK) {
				calendar.add(Calendar.DATE, cycleTime * 7);
			} else if (cycleType == RoseTaskBean.CYCLE_MONTH) {
				calendar.add(Calendar.MONTH, cycleTime);
			}
		}
		return calendar.getTime();
	}

}
